<!--
 * @author luguoxiang
 * @date 2022/9/17
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
-->
<template>
  <div style="margin-bottom: 16px; display: flex; align-items: center">
    <t-button size="small" @click="all()">全选</t-button>
    <t-button size="small" @click="clear()">全不选</t-button>
    <t-input :value="keyword" style="margin-left: var(--td-comp-margin-s)" @change="onSearch" />
  </div>
  <div v-if="data.length" style="display: flex; height: calc(100vh - 180px)" class="menu-select">
    <div style="width: 250px; height: 100%; overflow: auto">
      <t-collapse :value="[activeSys.value]" expand-mutex @change="clickSystem">
        <t-collapse-panel v-for="x in data" :key="x.key" :value="x.value">
          <template #header>
            <t-checkbox :checked="x.checked" :indeterminate="x.indeterminate" @click.stop @change="change(x, $event)" />
            {{ x.label }}
          </template>
          <directory
            :data="x.ds"
            :click-check="false"
            :active="activeDir.value"
            @click-node="clickDir"
            @change="change"
          />
        </t-collapse-panel>
      </t-collapse>
    </div>
    <div style="flex: 1; padding-left: 16px; height: 100%; display: flex; flex-direction: column">
      <t-breadcrumb max-item-width="150" style="padding: var(--td-comp-paddingTB-m) var(--td-comp-paddingLR-l)">
        <t-breadcrumbItem>{{ activeSys.label }}</t-breadcrumbItem>
        <t-breadcrumbItem v-for="x in breadcrumb" :key="x.key" @click="clickDir(x)">{{ x.label }}</t-breadcrumbItem>
      </t-breadcrumb>
      <lg-auto-size>
        <table class="gl-table" :key="permitKey">
          <tr class="gl-table-header">
            <th style="width: 180px">菜单</th>
            <th>
              <t-space>
                <span>权限</span>
                <t-checkbox
                  v-for="x in permits"
                  :key="x.label"
                  :checked="x.status === 2"
                  :indeterminate="x.status === 1"
                  @change="batchSet(x, $event)"
                >
                  {{ x.label }}
                </t-checkbox>
              </t-space>
            </th>
          </tr>
          <template v-if="menuList.length">
            <tr v-for="x in menuList" :key="x.key">
              <td>
                <t-checkbox :checked="x.checked" :indeterminate="x.indeterminate" @change="change(x, $event)">
                  {{ x.label }}
                </t-checkbox>
              </td>
              <td>
                <div class="t-checkbox-group">
                  <t-checkbox
                    v-for="p in x.ps"
                    :key="p.key"
                    :checked="p.checked"
                    :indeterminate="p.indeterminate"
                    @change="change(p, $event)"
                  >
                    {{ p.label }}
                  </t-checkbox>
                </div>
              </td>
            </tr>
          </template>
          <tr v-else>
            <td colspan="2">
              <div class="gl-empty">暂无数据</div>
            </td>
          </tr>
        </table>
      </lg-auto-size>
    </div>
  </div>
  <div v-else class="gl-empty">暂无数据</div>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import Directory from './Directory.vue';
import menuApi from '../api';
import { deepCopy, forEach, LgAutoSize, nextId, query } from '@/soar';

const props = defineProps({
  modelValue: {
    type: Array,
    default() {
      return [];
    },
  },
  immediate: {
    type: Boolean,
    default: true,
  },
});

const emits = defineEmits(['update:modelValue']);

function changeChecked(list: any[]) {
  for (const x of list) {
    if (x.children?.length) {
      changeChecked(x.children);
      let count = 0;
      for (const c of x.children) {
        if (c.indeterminate) {
          count = x.children.length + 1;
          break;
        }
        if (c.checked) {
          count++;
        }
      }

      if (count === 0) {
        x.indeterminate = false;
        x.checked = false;
      } else if (count === x.children.length) {
        x.indeterminate = false;
        x.checked = true;
      } else {
        x.indeterminate = true;
        x.checked = false;
      }
    } else {
      x.indeterminate = false;
      x.checked = props.modelValue?.includes(x.value);
    }
    x.key = nextId();
  }
}

const keyword = ref('');
function onSearch(e: string) {
  keyword.value = e;
  const tree = query(deepCopy(source), (x: any) => x.label.includes(e));
  handleTree(tree);
  changeChecked(tree);
  data.value = tree;
  menuList.value = [];
  if (tree.length) {
    clickSystem([tree[0]?.value]);
  }
}

function change(node: any, checked: boolean) {
  const selection = props.modelValue;
  if (checked) {
    forEach([node], (x) => {
      if (!selection.includes(x.value)) {
        selection.push(x.value);
      }
    });
  } else {
    forEach([node], (x) => {
      const index = selection.indexOf(x.value);
      if (index !== -1) {
        selection.splice(index, 1);
      }
    });
  }
  changeChecked(data.value);
  for (const p of permits.value) {
    const num = p.children.filter((x: any) => x.checked).length;
    p.status = num === 0 ? 0 : num === p.children.length ? 2 : 1;
  }

  emits(
    'update:modelValue',
    selection.filter((x: string) => !x.startsWith('0-')),
  );
}

function all() {
  data.value.forEach((x: any) => {
    change(x, true);
  });
}

function clear() {
  data.value.forEach((x: any) => {
    change(x, false);
  });
}

const activeSys = ref({});
function clickSystem(e) {
  activeSys.value = data.value.find((x) => x.value === e[0]) ?? {};
  clickDir(activeSys.value.ds?.[0] ?? {});
}

const breadcrumb = ref([]);
const activeDir = ref({});
const menuList = ref([]);
function clickDir(dir) {
  dir.expanded = true;
  if (dir.ds?.length) {
    clickDir(dir.ds[0]);
    return;
  }
  activeDir.value = dir;
  menuList.value = dir.ms ?? [];
  breadcrumb.value = getParent(dir);
}

function getParent(dir) {
  if (dir.parent?.type === 2) {
    return getParent(dir.parent).concat(dir);
  }
  return [dir];
}

function handleTree(tree: any[], parent = null) {
  for (const x of tree) {
    x.key = nextId();
    x.parent = parent;
    if (!x.children?.length) {
      delete x.ds;
      delete x.ms;
      delete x.ps;
      continue;
    }
    handleTree(x.children, x);
    const ps = [];
    const ms = [];
    const ds = [];
    for (const item of x.children) {
      switch (item.type) {
        case 0:
          ps.push(item);
          break;
        case 1:
          ms.push(item);
          break;
        case 2:
          ds.push(item);
          break;
        default:
          break;
      }
    }
    const dsLength = ds.length;

    if (ps.length) {
      const menu = {
        type: 1,
        value: `0-${x.value}-0`,
        label: '默认菜单',
        children: ps,
        parent: x,
        ps,
      };
      for (const p of ps) {
        p.parent = menu;
      }
      ms.unshift(menu);
    }

    if (ms.length) {
      const dir = {
        type: 2,
        value: `0-${x.value}`,
        label: '默认目录',
        children: ms,
        parent: x,
        ms,
      };
      for (const m of ms) {
        m.parent = dir;
      }
      ds.unshift(dir);
    }

    switch (x.type) {
      case 3:
        x.ds = ds;
        x.children = ds;
        break;
      case 2:
        if (dsLength) {
          x.ds = ds;
          x.children = ds;
        } else {
          x.ms = ms;
          x.children = ms;
        }
        break;
      case 1:
        x.ps = ps;
        x.children = ps;
        break;
      default:
        break;
    }
  }
}

function toTreeData(list: any[], pid: string) {
  const res = [];
  for (const x of list) {
    if (x.pId === pid) {
      x.children = toTreeData(list, x.value);
      res.push(x);
    }
  }
  return res;
}

let source = [];
const data = ref([]);

function load() {
  return menuApi
    .query({ $soarQuery: { fields: 'id,title,parentId,type', orderBys: 'sort' }, queryType: 'list', pageSize: 999999 })
    .then((res) => {
      // 数据转换
      res = res.list.map((x: { type: any; id: any; title: any; parentId: string }) => {
        return {
          type: x.type,
          value: x.id,
          label: x.title,
          pId: x.parentId,
          checked: false,
          indeterminate: false,
        };
      });
      let tree = toTreeData(res, '0');
      let systems = tree.filter((x) => x.type !== 3);
      systems = systems.filter((x) => x.type === 2);
      const defSys = {
        type: 3,
        value: '0',
        label: '默认系统',
        children: systems,
      };
      tree = tree.filter((x) => x.type === 3);
      tree.unshift(defSys);
      source = deepCopy(tree);

      handleTree(tree);
      changeChecked(tree);
      data.value = tree;
      clickSystem([tree[0]?.value]);
      return res;
    });
}

watch(
  () => props.modelValue,
  () => {
    changeChecked(data.value);
  },
  { immediate: true },
);

if (props.immediate) {
  load();
}

const permitKey = ref(0);
const permits = ref([]);
function batchSet(e: any, checked: boolean) {
  for (const p of e.children) {
    change(p, checked);
  }
  permitKey.value = Math.random();
}

watch(
  () => menuList.value,
  (ms) => {
    const ps: { label: string; status: 0 | 1 | 2; children?: any[] }[] = [];
    const mp: { [k: string]: any[] } = {};
    for (const menu of ms) {
      if (Array.isArray(menu.ps)) {
        for (const p of menu.ps) {
          if (ps.findIndex((x) => x.label === p.label) === -1) {
            ps.push({
              label: p.label,
              status: 0,
            });
            mp[p.label] = [];
          }
          mp[p.label].push(p);
        }
      }
    }
    const list: any[] = [];
    for (const p of ps) {
      if (mp[p.label].length / ms.length > 0.6) {
        p.children = mp[p.label];
        const num = p.children.filter((x: any) => x.checked).length;
        // eslint-disable-next-line no-nested-ternary
        p.status = num === 0 ? 0 : num === p.children.length ? 2 : 1;
        list.push(p);
      }
    }
    permits.value = list;
    permitKey.value = Math.random();
  },
  { immediate: true },
);
defineExpose({
  load,
});
</script>

<style lang="less" scoped>
.menu-select {
  :deep(.t-collapse-panel__content) {
    padding-left: 12px;
  }
}
</style>
